package com.artnology.independence.views;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ICheckStateProvider;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;

import com.artnology.independence.Activator;
import com.artnology.independence.model.DVPackage;
import com.artnology.independence.model.DVRootPackage;
import com.artnology.independence.model.DependencyGraph;
import com.artnology.independence.views.operations.AddNodeToGraphOperation;
import com.artnology.independence.views.operations.RemoveNodeFromGraphOperation;

public class DVNodeTreeViewer extends CheckboxTreeViewer implements
		IGraphModificationListener {

	public DVNodeTreeViewer(Composite parent) {
		super(parent);
		GridData gData = new GridData(GridData.FILL_BOTH);
		gData.horizontalSpan = 2;
		gData.grabExcessHorizontalSpace = true;
		getTree().setLayoutData(gData);
		setContentProvider(new DVPackageTreeContentProvider());
		setLabelProvider(new DVPackageTreeLabelProvider());
		setCheckStateProvider(new ICheckStateProvider() {

			@Override
			public boolean isGrayed(Object arg0) {
				DVPackage pack = (DVPackage) arg0;
				return pack.getVisibleContainedTypes().size() < pack
						.getContainedTypes().size();
			}

			@Override
			public boolean isChecked(Object arg0) {
				DVPackage pack = (DVPackage) arg0;
				return !pack.hasNoVisibleContainedType();
			}
		});

		addCheckStateListener(new ICheckStateListener() {

			@Override
			public void checkStateChanged(CheckStateChangedEvent event) {
				DVPackage checkedParentPackage = (DVPackage) event.getElement();
				IUndoableOperation operation = null;
				if (event.getChecked()) {
					operation = new AddNodeToGraphOperation("Check Package "
							+ checkedParentPackage.getName(), DependencyView
							.getView().getDependencyViewer()
							.getDependencyGraph(),
							checkedParentPackage
									.getContainedTypesWithSubpackages());
				} else {
					operation = new RemoveNodeFromGraphOperation("Uncheck "
							+ checkedParentPackage.getName(), DependencyView
							.getView().getDependencyViewer()
							.getDependencyGraph(),
							checkedParentPackage
									.getContainedTypesWithSubpackages());
				}

				operation.addContext(DependencyView.undoContext);
				try {
					DependencyView.getOperationHistory().execute(operation,
							null, null);
				} catch (ExecutionException e) {
					e.printStackTrace();
				}
			}
		});

		setComparator(new ViewerComparator() {
			@Override
			public int compare(Viewer viewer, Object e1, Object e2) {
				DVPackage p1 = (DVPackage) e1;
				DVPackage p2 = (DVPackage) e2;
				return p1.getName().compareTo(p2.getName());
			}
		});

		setInput(Activator.getDefault().getModel().getTopLevelPackages());
		expandAll();
		refresh();

	}

	@Override
	public void graphUpdated(DependencyGraph updatedGraph) {
		Display.getDefault().syncExec(new Runnable() {
			@Override
			public void run() {
				refresh();
				expandAll();
			}
		});
	}

	class DVPackageTreeContentProvider implements ITreeContentProvider {

		@Override
		public void dispose() {
		}

		@Override
		public void inputChanged(Viewer arg0, Object arg1, Object arg2) {
		}

		@Override
		public Object[] getChildren(Object arg0) {
			DVPackage pack = (DVPackage) arg0;
			return pack.getChildren().toArray();
		}

		@Override
		public Object[] getElements(Object arg0) {
			return new Object[] { Activator.getDefault().getModel()
					.getRootPackage() };
		}

		@Override
		public Object getParent(Object arg0) {
			return ((DVPackage) arg0).getParentPackage();
		}

		@Override
		public boolean hasChildren(Object arg0) {
			return ((DVPackage) arg0).getChildren().size() > 0;
		}

	}

	class DVPackageTreeLabelProvider implements ILabelProvider, IColorProvider {

		private Map<ImageDescriptor, Image> imageCache = new HashMap<ImageDescriptor, Image>();

		@Override
		public void addListener(ILabelProviderListener arg0) {
		}

		@Override
		public void dispose() {
		}

		@Override
		public boolean isLabelProperty(Object arg0, String arg1) {
			return false;
		}

		@Override
		public void removeListener(ILabelProviderListener arg0) {
		}

		@Override
		public Image getImage(Object arg0) {
			DVPackage pack = (DVPackage) arg0;
			ImageDescriptor descriptor = null;
			if (pack instanceof DVRootPackage) {
				descriptor = Activator
						.getImageDescriptor("icons/modelroot.gif");
			} else {
				descriptor = Activator
						.getImageDescriptor("icons/package_obj.gif");
			}
			Image image = (Image) imageCache.get(descriptor);
			if (image == null) {
				image = descriptor.createImage();
				imageCache.put(descriptor, image);
			}
			return image;
		}

		@Override
		public String getText(Object arg0) {

			DVPackage pack = (DVPackage) arg0;
			String text = pack.getLabel();
			if (!(pack instanceof DVRootPackage)) {
				text += " (" + pack.getVisibleContainedTypes().size() + ")";
			}
			return text;
		}

		@Override
		public Color getBackground(Object arg0) {
			DVPackage pack = (DVPackage) arg0;
			if (pack.getVisibleContainedTypes().size() == 0) {
				return convertAwtToSwtColor(java.awt.Color.WHITE);
			}
			java.awt.Color awtColor = ((DVPackage) arg0).getNodeFillColor();
			return convertAwtToSwtColor(awtColor);
		}

		@Override
		public Color getForeground(Object arg0) {
			DVPackage pack = (DVPackage) arg0;
			if (pack instanceof DVRootPackage) {
				return convertAwtToSwtColor(java.awt.Color.BLACK);
			}

			if (pack.getVisibleContainedTypes().size() == 0) {
				return convertAwtToSwtColor(java.awt.Color.LIGHT_GRAY);
			}
			java.awt.Color awtColor = ((DVPackage) arg0).getNodeFillColor();
			int threshhold = 180;
			if (awtColor.getRed() < threshhold
					&& awtColor.getGreen() < threshhold
					&& awtColor.getBlue() < threshhold) {
				return convertAwtToSwtColor(java.awt.Color.WHITE);
			} else {
				return convertAwtToSwtColor(java.awt.Color.BLACK);
			}
		}

	}

	private final static Color convertAwtToSwtColor(java.awt.Color awtColor) {
		return new Color(Display.getDefault(), awtColor.getRed(),
				awtColor.getGreen(), awtColor.getBlue());
	}

}
